/* noiser - can4linux can driver test utility
 *
 * noiser generates random CAN messages, random by CAN ID, CAN message type
 * and CAN data
 *
 * (c) 2000-2020 Heinz-Jürgen Oertel
 *
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdbool.h>
#include <sys/ioctl.h>

#include <can4linux.h>

/* called without option, it uses /dev/<STDDEV> */
#define STDDEV "/dev/can1"
#define VERSION "2.1"

#define MAX_BLOCK_SIZE 50 
#define USE_RTR 1

#ifndef TRUE
# define TRUE  1
# define FALSE 0
#endif

#define MAXDEVNAME 40		/* max characters for CAN device name */
#define DEFAULT_CANFD_LEN   64  /* Only: 8,12,16,20,24,32,48,64 are allowed */

static int extd                 = FALSE;
static int rtr                  = FALSE;
static int baud			= 125;		/* default baud rate */
static bool debug               = false;
static int sleeptime            = 1000;		/* default: 1 s, in ms */
static unsigned int canfd	= 0;    /* use more than 8 bytes for CAN FD */ 
					/* if != 0 contains number of max bytes */

static void usage(char *s)
{
static const char *usage_text  = "\
generates blocks of random messages in intervalls\n\
\n\
Options:\n\
-n --blocksize=number of messages in one consecutive block\n\
-r --rtr      generate random rtr frames.\n\
-e --ext      generate random frames in extended message ID format.\n\
-s --sleep=n  sleeptime between messages in ms, if not specified send single message\n\
-d --debug    debug On\n\
-b --baud=baudrate (Standard wo option uses value of /proc/sys/Can/baud)\n\
-D dev        use /dev/dev/{can0,can1,can2,can3} (real nodes, std: can1)\n\
";
    fprintf(stderr, "usage: %s [options] [id [ byte ..]]\n", s);
    fprintf(stderr, "%s", usage_text);
}

/***********************************************************************
*
* set_bitrate - sets the CAN bit rate
*
*
* Changing these registers only possible in Reset mode.
*
* RETURN:
*
*/

static int	set_bitrate(
	int fd,			/* device descriptor */
	int lbaud		/* bit rate */
	)
{
config_par_t  cfg;
volatile command_par_t cmd;
int ret;


    cmd.cmd = CMD_STOP;
    ioctl(fd, CAN_IOCTL_COMMAND, &cmd);

    cfg.target = CONF_TIMING; 
    cfg.val1   = (unsigned int)lbaud;
    ret = ioctl(fd, CAN_IOCTL_CONFIG, &cfg);

    cmd.cmd = CMD_START;
    ioctl(fd, CAN_IOCTL_COMMAND, &cmd);

    if (ret < 0) {
	perror("set_bitrate");
	exit(-1);
    } else {
	ret = 0;
    }
    return ret;
}


/**
*
* The main program
*
*/
int main(int argc,char **argv)
{
int	fd;
int	i,sent;
canmsg_t tx[MAX_BLOCK_SIZE];
char	device[MAXDEVNAME] = STDDEV;
int	blocksize = 5;
char	*pname;
int	c;


    pname = *argv;
    /* parse command line */
    while (1) {
	static const struct option long_options[] =
	{
	    /* These options set a flag. */
	    {"ext",     no_argument,       NULL,    'e'},
	    {"rtr",     no_argument,       NULL,    'r'},
	    {"version", no_argument,       NULL,    'V'},
	    {"help",    no_argument,	   NULL,    'h'},
	    /* These options don't set a flag.
	      We distinguish them by their indices. */
	    {"baud",    required_argument, NULL,    'b'},
	    {"canfd",   optional_argument, NULL,   200 },
	    {"debug",   optional_argument, NULL,    'd'},
	    {"sleep",   required_argument, NULL,    's'},
	    {"blocksize", required_argument, NULL,  'n'},
	    {0, 0, 0, 0}
	};
	/* getopt_long stores the option index here. */
	int option_index = 0;


	c = getopt_long(argc, argv, "b:dehl:rs:n:D:t:T:VR",
		    long_options, &option_index);
	if (c == -1) {
             break;
	}
	switch (c) {
	    case 'r':
		rtr = TRUE;
		break;
	    case 'e':
		extd = TRUE;
		break;
	    case 'b':
		baud = atoi(optarg);
		break;
	    case 's':
		sleeptime = atoi(optarg);
		break;
	    case 'D':
		if(
		    /* path ist starting with '.' or '/', use it as it is */
			optarg[0] == '.'
			|| 
			optarg[0] == '/'
			) {
		    snprintf(device, MAXDEVNAME, "%s", optarg);

	        } else {
		    snprintf(device, MAXDEVNAME, "/dev/%s", optarg);
		}
		break;
	    case 'd':
		debug = TRUE;
		break;
	    case 'n':	/* Blocksize */
		blocksize = strtoul(optarg, NULL, 0);
		if(blocksize > MAX_BLOCK_SIZE) {
		    fprintf(stderr, "max blocksize is %d, set to %d\n",
				MAX_BLOCK_SIZE, MAX_BLOCK_SIZE);
		    blocksize = MAX_BLOCK_SIZE;
		}
		break;
	    case 'V':
		printf("noiser V " VERSION ", " __DATE__ "%s \n",
#if defined(CANFD)
			    " (CAN FD data structure used)"
#else
			    ""
#endif
			);
		printf(" can4linux.h header version %d.%d\n",
			CAN4LINUXVERSION >> 8, CAN4LINUXVERSION & 0xFF);
		exit(0);
	    case 200:		/* --canfd= */
		// printf(" used CAN-FD option, index %d, arg: %s\n", option_index, optarg);
		/* check for allowed values
		    dlc	    bytes
		    9	    12
		    10	    16
		    11	    20
		    12	    24
		    13	    32
		    14	    48
		    15	    64
		 */
		if(optarg != NULL) {
		    canfd = atoi(optarg);
		    switch(canfd) {
			case  8:
			case 12:
			case 16:
			case 20:
			case 24:
			case 32:
			case 48:
			case 64:
			    break;
			default:
			    printf(
			    "CAN FD data length %d not allowed. Only: 8,12,16,20,24,32,48,64\n",
			    canfd);
			    exit(1);
		    }
		} else {
		    canfd = DEFAULT_CANFD_LEN;
		}

		break;
	    case 'h':
	    default: usage(pname);
		exit(0);
	}   /* end switch() */
    }	    /* end while() */


    if(debug == true) {
	printf("using CAN device %s with %d kbit/s\n", device, baud);
	printf("generated blocksize is %d messages\n", blocksize);
	printf("Sleep time between block of transmitted messages= %d ms\n", sleeptime);
    }

    sleeptime *= 1000; /* given as argument in ms, but internal use is µs */
    
    if(( fd = open(device, O_RDWR )) < 0 ) {
	fprintf(stderr, "Error opening CAN device %s\n", device);
        exit(1);
    }
    if (baud > 0) {
	if (debug == true) {
	    printf("change Bit-Rate to %d Kbit/s\n", baud);
	}
	set_bitrate(fd, baud);
    }

    // exit(0);

    while(true) {
      /* fill message block with data */
      tx[0].id    = 1 + (int)( 500.0 * rand() / RAND_MAX + 1.0);
      for(i = 0; i < blocksize; i++) {
	int end;
        tx[i].flags = 0;
	/* max CAN ID is  2048 */
	tx[i].id    = 1 + (int)( 2044.0 * rand() / RAND_MAX + 1.0);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-truncation="
	/* we need the longer const char *format string her, it should be
	 * reduced to 8 byte, it will be different for CAN FD frames
	 * ToDo: CAN FD frames */
	snprintf( (char *)tx[i].data, 8, "%u:abcdefgh", tx[i].id);
	/* end should be between 0 and 8 */
#pragma GCC diagnostic pop
	end = (int)(9.0 * rand()/ RAND_MAX);
	tx[i].data[end] = '\0';  /* mark end of data field */
	
	if(debug == true) printf("send:%d, 0x%08x '%s' \n",
		end, tx[i].id, tx[i].data);
	tx[i].length = strlen((char *)tx[i].data); 

	if(rtr == true) {
	    if(    tx[i].id == 500
		|| tx[i].id == 450
		|| tx[i].id == 400
		|| tx[i].id == 350
		|| tx[i].id == 300
		|| tx[i].id == 250
		|| tx[i].id == 200
		|| tx[i].id == 150 
		|| tx[i].id == 100 
		|| tx[i].id ==  50 )
		tx[i].flags |= MSG_RTR;
        }
	if(extd == true) {
	    if((tx[i].id % 9) == 5) 
		tx[i].flags |= MSG_EXT;
	}
      }
	sent=write(fd, tx, blocksize);
	if(debug == true) printf("-------------------\n");
	if(sleeptime > 0) usleep(sleeptime);
	if(sent <= 0) {
	    printf("not ready"); break;
	}
    }
    close(fd);
    return 0;
}
